<html>
    <head>
        <title>后台工作者 Background Worker</title>
    </head>
    <body>
        <script>
            // 后台任务 Background Worker 

                    /*
                        一、导言

                                后台任务是在后台运行的应用程序中"简单的独立线程"。

                                通常，它们定期运行以执行一些任务。

                                例子：
                                        ①、后台任务可以定期运行删除旧日志

                                        ②、后台任务可以定期运行以确定低活用户并发送电子邮件以让用户返回你的应用程序。
                    */

                    /*
                        二、创建后台任务

                                后台任务应该间接或者直接的实现 IBackgroundWorker接口。

                                后天任务本质上是单例的(Singleton). 因此，你的后台任务类只有一个实例被实例化并运行。


                                1、BackgroundWorkerBase 基类

                                     派生自BackgroundWorkerBase基类，是创建后台任务的简单方法。
                                     
                                        // 继承自 BackgroundWorkerBase 基类，是实现后台任务的最简单方法。
                                        public class MyWorker : BackgroundWorkerBase
                                        {
                                            // 重写StartAsync 启动后台任务
                                            public override Task StartAsync(CancellationToken cancellationToken = default)
                                            {
                                                // 启动时候要做的事情
                                            }

                                            // 重写 StopAsync 停止后台任务
                                            public override Task StopAsync(CancellationToken cancellationToken = default)
                                            {
                                                // 停止时候要做的事情
                                                }
                                        }

                                        你可以直接实现IBackgroundWorker，但是BackgroundWorkerBase提供了一些有用的属性，如Logger。


                                2、 AysncPeriodicBackgroundWorkerBase （异步间歇性后台任务基类）
                                
                                        假如如果一个用户30天内没有登录，我们想激活用户活性。

                                        AsyncPeriodicBackgroundWorkerBase类简化了创建周期性后台任务的过程：

                                            // 声明周期性后台任务类
                                            public class PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase // 周期性后台任务基类
                                            {
                                                // 构造函数
                                                public PassiveUserCheckerWorker(
                                                        AbpAsyncTimer timer, // 异步计时器
                                                        IServiceScopeFactory serviceScopeFactory // 服务作用域工厂
                                                    ) : base(timer, serviceScopeFactory)
                                                {
                                                    Timer.Period = 600000; //10 分钟
                                                }

                                                // 重写 DoWorkAsync 方法，执行后台任务
                                                protected async override Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext)
                                                {
                                                    Logger.LogInformation("Starting: Setting status of inactive users...");

                                                    //使用服务注入（不从构造函数中注入服务，因为使用了IServiceScope）
                                                    var userRepository = workerContext.ServiceProvider
                                                                                      .GetRequiredService<IUserRepository>();

                                                    // 执行后台任务逻辑
                                                    await userRepository.UpdateInactiveUserStatusesAsync();

                                                    Logger.LogInformation("Completed: Setting status of inactive users...");
                                                }
                                            }
                                                 
                                       ①、AsyncPeriodicBackgroundWorkerBase使用AbpAsyncTimer（一个线程安全计时器）对象来确定周期。 
                                          我们可以在构造函数中设置它的Period属性。

                                       ②、它需要实现DoWorkAsync方法来执行周期性工作。

                                       ③、从PeriodicBackgroundWorkerContext而不是构造函数注入解决依赖关系是一种很好的做法。

                                          因为AsyncPeriodicBackgroundWorkerBase使用IServiceScope，当你的工作完成时，已释放。

                                       ④、AsyncPeriodicBackgroundWorkerBase捕获并记录由DoWorkAsync方法抛出的异常。
                    */

                    /*
                        三、注册后台任务

                                创建后台任务类后，你应该将其添加到 IBackgroundWorkerManger (后台任务管理器)。

                                最常见的地方是模块类的 OnApplicationInitializationAsync 方法：

                                        [DependsOn(typeof(AbpBackgroundWorkersModule))]
                                        public class MyModule : AbpModule
                                        {
                                            // ABP程序初始化钩子
                                            public override async Task OnApplicationInitializationAsync(
                                                ApplicationInitializationContext context)
                                            {
                                                // 添加后台任务到后台任务管理器
                                                await context.AddBackgroundWorkerAsync<PassiveUserCheckerWorker>();
                                            }
                                        }
 
                                context.AddBackgroundWorkerAsync() 是下面表达式的快捷扩展方式：

                                        await context.ServiceProvider
                                                    .GetRequiredService<IBackgroundWorkerManager>()
                                                    .AddAsync(
                                                        context
                                                            .ServiceProvider
                                                            .GetRequiredService<PassiveUserCheckerWorker>()
                                                    );

                                因此，它解析给定的后台工作并添加到IBackgroundWorkerManager。

                                虽然我们通常在OnApplicationInitializationAsync中注册(添加)后台任务，但对此没有限制。

                                你可以在任何地方注入IBackgroundWorkerManager 并在运行时添加后台任务。

                                当你的应用程序被关闭时，后台工作人员管理器将停止并释放所有注册的后台任务。
                    */

                    /*
                        四、AbpBackgroundWorkerOptions 选项类  
                                                                
                                   AbpBackgroundWorkerOptions选项类用于为后台任务服务配置选项。

                                   目前，只有一个选项：
                                         IsEnabled（默认值：true）：用于为你的应用程序启用/禁用后台工作系统。
                                    
                    */

                    /*
                        五、让你的应用程序始终运行

                                  仅当你的应用程序正在运行时，后台任务才起作用。

                                  如果你在Web应用程序中托管运行后台任务（这是默认行为），则应该确保你的Web应用程序配置为始终运行。

                                  否则，后台任务仅在你的应用程序正在使用时起作用。
                    */

                    /*
                        六、在集群上运行
                        
                                如果你在集群环境中同时运行应用程序的多个实例，请小心。

                                在这种情况下，每个应用程序都运行相同的后台任务，如果你的后台任务在相同的资源上运行（例如，处理相同的数据），这可能会产生冲突。
                                                            
                                .... （更多详情看文档）
                    */

                    /*
                        七、集成

                                后台任务系统是可扩展的，你可以使用自己的实现或 "预构建"的集成更改默认后台任务管理器。
                                
                                请参阅预构建的工作管理器替代方案：

                                            Quartz 后台任务管理器
                                            
                                            Hangfire 后台任务管理器
                    */
        </script>
    </body>
</html>